/* eslint-disable camelcase */
import React, { useCallback, useState, useEffect, useRef, useMemo } from 'react';
import { Form, Modal, Input, Button, Row, message, ConfigProvider } from 'antd';
import zh_CN from 'antd/lib/locale-provider/zh_CN';
import PropTypes from 'prop-types';
import { const_initItemsFun, const_execAsyncFun, const_changeFormItems } from '../constant';
import ColFormItem from './ColFormItem';
import { dataType, turnLabelOrValue, GenNonDuplicateID } from '../zTool';
import moment from 'moment';
import classNames from 'classnames';
import './style.module.scss';


const propTypes = {
    className: PropTypes.string,
    labelLayout: PropTypes.string, //'horizontal'|'vertical' | 	inline
    items: PropTypes.arrayOf(PropTypes.object),
    getformInstance: PropTypes.func,
    onSubmit: PropTypes.func,
    onValidated: PropTypes.func, //表单验证之后,调用onSubmit之前
    formDefaultValues: PropTypes.object, //values 替代 formDefaultValues；相当于 formDefaultValues 改名为 values
    values: PropTypes.object,
    submitBtnName: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
    submitMsg: PropTypes.any,
    defaultSpan: PropTypes.oneOfType([PropTypes.number, PropTypes.object]),
    submitBtnRender: PropTypes.func,
    afterItemsRendered: PropTypes.func, // 表单控件渲染完的回调
    colContentRender: PropTypes.func, //每个col内除了控件还可插入其他内容
    otherForms: PropTypes.func, // 取得其他表单对象
    confirm: PropTypes.object, // antd 的 modal 参数
    // initAnimation: PropTypes.bool,
    momentFormat: PropTypes.bool,
    booleanToNumber: PropTypes.bool,
    controlSize: PropTypes.string,
    excludeHideValue: PropTypes.bool, //onSubmit的values是否不包含隐藏控件的值
    excludeHideValid: PropTypes.bool, //触发onSubmit后是否不验证隐藏控件的值
    hiddenItemFirstRendering: PropTypes.bool, //默认隐藏的控件是否初始就渲染
    onlySetCurrentValues: PropTypes.bool, //setFieldsValue时 是否 只对当前form设置值，排除otherForms方法的其他form
};


const defaultProps = {
    onlySetCurrentValues: false,
    excludeHideValue: true,
    excludeHideValid: true,
    hiddenItemFirstRendering: false,
    confirm: {},
    items: [
        {
            lable: '字段名',
            key: 'name',
            options: {},
            render: (form, panel) => <Input />,
        },
    ],
    submitMsg: '点击确定按钮提交数据',
    defaultSpan: { xxl: 6, xl: 8, lg: 12, md: 24 },
    submitBtnName: '保存',
    labelLayout: 'vertical',
    // initAnimation: true,
    momentFormat: false,
    booleanToNumber: true,
    controlSize: 'default',
};


const Zform = (props) => {
    const { values, formDefaultValues, submitBtnName, className, style, submitBtnRender, labelLayout, controlSize, children } = props;
    const [items, setItem] = useState([]);
    const [saveSettingValues, setsSveSettingValues] = useState({});
    const formkey = useMemo(() => GenNonDuplicateID(), []);
    const [formInstanc] = Form.useForm();
    const allAsync = useRef([]);
    const prevPropsForm = useRef({});
    const prevStateItems = useRef([]);
    const prevSettingValues = useRef({});
    const initSetValue = useRef(false);
    const methods = useRef({});
    const unmounted = useRef(false);
    const [form, setForm] = useState({
        ...formInstanc,
        zformItems: [],
        saveFieldOptions: {},
        saveOptionsMapKey: {},
        getAsyncQueue: () => allAsync.current,
        saveSettingValues: {},
    });

    const setFieldsValue = (vals) => {
        methods.current.setFieldsValue(vals);
    };

    const handleConst_execAsyncFun = useCallback((newItems) => {
        const_execAsyncFun({
            allAsync,
            unmounted,
            items: newItems,
            setFieldsValue,
            form: formInstanc,
            methods: methods.current,
            setItem,
        }, (...rest) => {
            setForm({
                ...form,
                formReady: true,
            });
            props.afterItemsRendered && props.afterItemsRendered(...rest);
        });
    }, [form, formInstanc, props]);

    const execAsync = useCallback((newItems) => {
        const formItems = Array.isArray(newItems) ? newItems : props.items;
        if (!formItems.length) {
            setItem(formItems);
            return;
        }
        const_initItemsFun({
            items: formItems,
            renderArgument: form,
            allAsync,
            setItem,
            changeItems: methods.current.changeFormItems,
            defaultSpan: props.defaultSpan,
            labelLayout,
            hiddenItemFirstRendering: props.hiddenItemFirstRendering,
            controlSize,
        }, (newItems) => {
            handleConst_execAsyncFun(newItems);
        });
    }, [controlSize, form, handleConst_execAsyncFun, labelLayout, props.defaultSpan, props.hiddenItemFirstRendering, props.items]);

    const setNewForm = useCallback(() => {
        if (
            formInstanc !== prevPropsForm.current ||
            items !== prevStateItems.current ||
            saveSettingValues !== prevSettingValues.current
        ) {
            const newForm = {
                ...formInstanc,
                zformItems: items,
                saveFieldOptions: form.saveFieldOptions,
                saveOptionsMapKey: form.saveOptionsMapKey,
                getAsyncQueue: () => allAsync.current,
                saveSettingValues: saveSettingValues || {},
            };
            setForm(newForm);
            prevSettingValues.current = saveSettingValues;
            prevPropsForm.current = formInstanc;
            prevStateItems.current = items;
            props.getFormInstance && props.getFormInstance(newForm, methods.current);
        }
    }, [form, formInstanc, items, props, saveSettingValues]);

    useEffect(() => {
        setNewForm();
        execAsync();
        return () => {
            //组件卸载标识，用在异步回调阻止任何setState操作
            unmounted.current = true;
        };
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, []);

    useEffect(() => {
        setNewForm();
    }, [items, form, setNewForm]);
    useEffect(() => {
        if (!allAsync.current.length) {
            setNewForm();
            execAsync();
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [props.items]);

    useEffect(() => {
        if (items.length && (Object.keys(values || {}).length || Object.keys(formDefaultValues || {}).length) && !initSetValue.current) {
            setFieldsValue();
            execAsync();
            initSetValue.current = true;
        }
    }, [items, values, formDefaultValues, execAsync]);



    useEffect(() => {
        if (!allAsync.current.length && initSetValue.current) {
            setFieldsValue();
            execAsync();
        }
    // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [values, formDefaultValues]);


    const onFirstRender = (item) => {
        const val = methods.current.treatValue({
            key: item.key,
            value: saveSettingValues[item.key],
            formItems: items,
        });

        if (val !== undefined && val !== null) {
            form.setFieldsValue({
                [item.key]: val,
            });
        }
    };

    const getAnyMethods = () => methods.current;
    const getFormItems = () => {
        setNewForm();
        const formItems = items.map((item, i) => {
            const colItem = (
                <ColFormItem
                    formkey={formkey}
                    hiddenItemFirstRendering={props.hiddenItemFirstRendering}
                    key={item.key}
                    valuelabel={saveSettingValues[`${item.key}Label`]}
                    colContentRender={props.colContentRender}
                    loading={item.loading}
                    form={form}
                    changeFormItems={methods.current.changeFormItems}
                    item={item}
                    ref={item.ref}
                    labelLayout={props.labelLayout}
                    getInsideItems={methods.current.getInsideItems}
                    controlSize={props.controlSize}
                    onFirstRender={onFirstRender}
                    getAnyMethods={getAnyMethods}
                />
            );
            return colItem;
        });
        return formItems;
    };
    methods.current = useMemo(() => ({
        getForms: () => {
            const forms = dataType.isFunction(props.otherForms)
                ? props.otherForms(form).concat([form])
                : [form];
            return forms;
        },
        getFieldsValue: (isValid, query) => {
            const forms = methods.current.getForms();
            let formsValues = [];
            const valideds = forms.map((form) => {
                //保存的对应key的选项数据
                const saveFieldOptions = form.saveFieldOptions;
                const saveOptionsMapKey = form.saveOptionsMapKey;
                // console.log("--form--",form)
                let valided = true;
                const otherFormItems = props.getOtherFormItems ? props.getOtherFormItems() : [];
                const formitems = form.zformItems.concat(otherFormItems);
                const filterItems = [];
                const hiddenRenderingItems = [];

                formitems.forEach((item) => {
                    const hiddenRendering = Object.prototype.hasOwnProperty.call(item, 'hiddenRendering')
                        ? !!item.hiddenRendering
                        : props.hiddenItemFirstRendering;
                    if (hiddenRendering) {
                        hiddenRenderingItems.push(item);
                    }
                    if (props.excludeHideValid) {
                        if (item.ref.current?.itemShowStatus) {
                            filterItems.push(item);
                        }
                    } else {
                        filterItems.push(item);
                    }
                });

                const fieldNames = filterItems.map((item) => item.key);
                let validPromise = null;
                if (isValid) {
                    if (isValid === 'asyncValid') {
                        //异步验证
                        validPromise = new Promise((resolve, reject) => {
                            form.validateFields(fieldNames).then((value) => {
                                resolve(value);
                            }).catch((err) => {
                                if (err.errorFields.length) {
                                    reject(err);
                                }
                            });
                        });
                    } else {
                        form.validateFields(fieldNames).catch((err) => {
                            if (err.errorFields.length) {
                                valided = false;
                            }
                        });
                    }
                }
                const getEndValues = () => {
                    const names = props.excludeHideValue
                    ? [...fieldNames, ...hiddenRenderingItems.map((item) => item.key)]
                    : null;
                    const currentValues = form.getFieldsValue(names);
                    const newValues = methods.current.getOneFormValue(
                        currentValues,
                        saveFieldOptions,
                        saveOptionsMapKey,
                        form.zformItems,
                    );
                    return {
                        ...newValues,
                        ...(dataType.isObject(query) ? query : {}),
                    };
                };
                if (validPromise) {
                    return new Promise((resolve, reject) => {
                        validPromise
                            .then(() => {
                                resolve(getEndValues());
                            })
                            .catch(reject);
                    });
                }

                formsValues.push(getEndValues());
                return valided;
            });
            if (isValid === 'asyncValid') {
                //异步验证
                const endPromise = new Promise((resolve, reject) => {
                    const otherValues = props.getOtherFieldsValue
                        ? props.getOtherFieldsValue(isValid, formsValues)
                        : Promise.resolve({});
                    otherValues
                        .then((ov) => Promise.all(valideds)
                                .then((validValues) => {
                                    formsValues = validValues.length > 1 ? validValues : validValues[0];
                                    if (dataType.isObject(ov) && Object.keys(ov).length) {
                                        if (Array.isArray(formsValues)) {
                                            formsValues.push(ov);
                                        } else {
                                            Object.keys(ov).forEach((key) => {
                                                formsValues[key] = ov[key];
                                            });
                                        }
                                    }
                                    resolve(formsValues);
                                })
                                .catch(() => {
                                    message.error('表单未通过验证,请检查');
                                    reject();
                                }))
                        .catch(reject);
                });
                return endPromise;
            }
            //有一个验证失败，就阻止
            if (valideds.some((valid) => !valid)) {
                message.error('表单未通过验证,请检查');
                return false;
            }
            const otherValues = props.getOtherFieldsValue
                ? props.getOtherFieldsValue(isValid, formsValues)
                : {};
            const lastValues = formsValues.length > 1 ? formsValues : formsValues[0];
            if (dataType.isObject(otherValues) && Object.keys(otherValues).length) {
                if (Array.isArray(lastValues)) {
                    lastValues.push(otherValues);
                } else {
                    Object.keys(otherValues).forEach((key) => {
                        lastValues[key] = otherValues[key];
                    });
                }
            } else if (!otherValues) {
                return false;
            }
            return lastValues;
        },
        treatValue: ({ key, value, formItems }) => {
            if (value !== undefined) {
                let newValue = value;
                if (props.momentFormat) {
                    const currentItem = formItems.find((item) => item.key === key);
                    if (currentItem && currentItem.format) {
                        const toMoment = (val, format) => moment(val, format);
                        if (dataType.isArray(value)) {
                            newValue = value.map((val) => val ? toMoment(val, currentItem.format) : undefined);
                        } else {
                            newValue = value ? toMoment(value, currentItem.format) : undefined;
                        }
                    } else {
                        newValue = value;
                    }
                }
                return newValue;
            }
        },
        setFieldsValue: (vals) => {
            const values = vals || props.values || props.formDefaultValues;
            if (values) {
                const newsaveSettingValues = { ...(saveSettingValues || {}), ...(values || {}) };
                setsSveSettingValues(newsaveSettingValues);
            }
            const forms = !props.onlySetCurrentValues ? methods.current.getForms() : [form];
            const otherFormItems = (props.getOtherFormItems && props.getOtherFormItems()) || [];
            forms.forEach((form) => {
                const formItems = form.zformItems.concat(otherFormItems);
                if (values && formItems.length) {
                    const newValues = {};
                    formItems.forEach((field) => {
                        const key = field.key;
                        const value = values[key];
                        if (value !== undefined) {
                            newValues[key] = methods.current.treatValue({ key, value, formItems });
                        }
                    });
                    const allKeys = Object.keys(newValues);
                    if (allKeys.length) {
                        form.setFieldsValue(newValues);
                    }
                }
            });
        },
        setFields: (vals) => {
            const values = vals;
            const forms = methods.current.getForms();
            const otherFormItems = props.getOtherFormItems ? props.getOtherFormItems() : [];
            forms.forEach((form) => {
                const formItems = form.zformItems.concat(otherFormItems);
                if (values && formItems.length) {
                    const newValues = {};
                    formItems.forEach((field) => {
                        const key = field.key;
                        const fieldValue = values[key];
                        if (fieldValue !== undefined) {
                            newValues[key] = fieldValue;
                            const value = fieldValue.value;

                            if (value !== undefined) {
                                newValues[key].value = methods.current.treatValue({ key, value, formItems });
                            }
                        }
                    });
                    const allKeys = Object.keys(newValues);
                    if (allKeys.length) {
                        form.setFields(newValues);
                    }
                }
            });
        },
        getOneFormValue: (values, saveFieldOptions, saveOptionsMapKey, items) => {
            const newValues = {};
            const otherFormItems = props.getOtherFormItems ? props.getOtherFormItems() : [];
            const formItems = (items || items).concat(otherFormItems);
            if (dataType.isObject(values)) {
                Object.keys(values).forEach((key) => {
                    if (props.excludeHideValue) {
                        //不包含隐藏并且非强制渲染的控件的值
                        const hideItem = formItems.some((item) => {
                            const hiddenRendering = item.hasOwnProperty('hiddenRendering')
                                ? !!item.hiddenRendering
                                : props.hiddenItemFirstRendering;
                            return item.key === key && !item.ref.current?.itemShowStatus && !hiddenRendering;
                        });

                        if (hideItem) {
                            return;
                        }
                    }
                    const isNotFormItem = formItems.find(
                        (item) => item.key === key && item.isFormItem !== undefined && !item.isFormItem,
                    );
                    if (isNotFormItem) {
                        return;
                    }
                    if (dataType.isString(values[key])) {
                        //字符串类型的值去掉首尾空格
                        newValues[key] = values[key].trim();
                        //凡是只有%_的都会转义
                        const hasSyml = newValues[key].match(/^[\%\_]+$/g);
                        if (hasSyml) {
                            //转义通配符% _
                            hasSyml.forEach((syml) => {
                                newValues[key] = newValues[key].replace(syml, `\\${syml}`);
                            });
                        }
                    } else if (props.momentFormat) {
                        //如果需要把moment对象格式化对应的format
                        const formating = (val) => {
                            if (dataType.isObject(val) && val._isAMomentObject) {
                                const currentItem = formItems.find((item) => item.key === key);
                                return val.format(currentItem.format);
                            }
                            return val;
                        };
                        if (dataType.isArray(values[key])) {
                            newValues[key] = values[key].map((val) => formating(val));
                        } else {
                            newValues[key] = formating(values[key]);
                        }
                    } else if (props.booleanToNumber && dataType.isBoolean(values[key])) {
                        newValues[key] = Number(values[key]);
                    } else {
                        newValues[key] = values[key];
                    }
                    if (
                        (newValues[key] !== null &&
                            newValues[key] !== undefined &&
                            !Array.isArray(newValues[key])) ||
                        (Array.isArray(newValues[key]) && newValues[key].length)
                    ) {
                        const options = (saveFieldOptions || {})[key] || [];
                        const mapKeys = {
                            label: 'label',
                            value: 'value',
                            children: 'children',
                            ...(saveOptionsMapKey[key] || {}),
                        };
                        const currentLabel = turnLabelOrValue(options, newValues[key], {
                            src: mapKeys.value,
                            dist: mapKeys.label,
                        });
                        const labelVal =
                            (Array.isArray(currentLabel) && currentLabel.length) ||
                                (!Array.isArray(currentLabel) && currentLabel)
                                ? currentLabel
                                : saveSettingValues[`${key}Label`];
                        if (labelVal) {
                            newValues[`${key}Label`] = labelVal;
                        }
                    }
                });
            }
            return newValues;
        },
        onSubmit: (e, query) => {
            e.preventDefault();
            e.stopPropagation();
            const forms = methods.current.getForms();
            if (
                forms.some((form) => form.zformItems.some((item) => (
                            item.ref.current && item.ref.current.itemShowStatus && item.ref.current.state.loading
                        )))
            ) {
                message.error('异步处理未完成，请稍后重试！');
                return;
            }
            const validPromise = methods.current.getFieldsValue('asyncValid', query);
            validPromise
                .then((newValues) => {
                    let pipe = Promise.resolve();
                    if (typeof props.onValidated === 'function') {
                        const forms = dataType.isFunction(props.otherForms)
                            ? props.otherForms(form).concat([form])
                            : [form];
                        const returned = props.onValidated({ forms, values: newValues });
                        if (dataType.isPromise(returned)) {
                            pipe = returned;
                        } else if (dataType.isBoolean(returned) && !returned) {
                            return;
                        } else if (dataType.isObject(returned)) {
                            if (dataType.isObject(newValues)) {
                                newValues = { ...newValues, ...returned };
                            } else if (dataType.isArray(newValues)) {
                                newValues.push(returned);
                            }
                        }
                    }
                    pipe.then((returned) => {
                        if (dataType.isBoolean(returned) && !returned) {
                            return;
                        } else if (dataType.isObject(returned)) {
                            if (dataType.isObject(newValues)) {
                                newValues = { ...newValues, ...returned };
                            } else if (dataType.isArray(newValues)) {
                                newValues.push(returned);
                            }
                        }
                        const { onOk, content, show, ...others } = props.confirm;
                        //submitMsg本可以弃掉，这里为了兼容，confirm的参数可以代替submitMsg
                        if (
                            (dataType.isBoolean(show) && show) ||
                            (props.submitMsg && !dataType.isBoolean(show))
                        ) {
                            Modal.confirm({
                                title: '确定好提交了吗?',
                                content: content || props.submitMsg,
                                onOk: (e) => props.onSubmit ? props.onSubmit(newValues) : Promise.resolve(),
                                ...others,
                            });
                        } else {
                            props.onSubmit && props.onSubmit(newValues);
                        }
                    });
                })
                .catch((res) => {
                    if (res) {
                        console.error(res);
                    }
                });
        },
        changeFormItems: (newItems, part = false, callback) => {
            const_changeFormItems({
                items,
                newItems,
                part,
                controlSize: props.controlSize,
                form,
                methods: methods.current,
                execAsync,
                callback
            },);
        },
        getInsideItems: () => items,
    }), [execAsync, form, items, props, saveSettingValues]);

    const setAntdConfigProvider = useCallback((children) => <ConfigProvider locale={zh_CN}>{children}</ConfigProvider>, []);

    if (typeof children === 'function') {
        return setAntdConfigProvider(children(form));
    }
    const itemsCom = getFormItems();
    const wrapperClassname = classNames('z-form', className || '');
    return setAntdConfigProvider(
        <Form form={form} onSubmit={methods.current.onSubmit} className={wrapperClassname} style={style}>
            <Row className={`z-form-row z-form-label-${labelLayout} z-form-control-${controlSize}`}>
                {itemsCom}
            </Row>
            {typeof submitBtnRender === 'function' ? (
                submitBtnRender(methods.current.onSubmit, props, methods.current)
            ) : submitBtnName ? (
                <div
                    className="z-text-center z-padding-15"
                    style={props.labelLayout === 'inline' ? { marginTop: '-15px' } : {}}>
                    <Button type="primary" htmlType="submit" icon="check">
                        {typeof submitBtnName === 'function' ? submitBtnName() : submitBtnName}
                    </Button>
                </div>
            ) : null}
        </Form>,
    );
};

Zform.propTypes = propTypes;
Zform.defaultProps = defaultProps;




export default Zform;
